package com.flca.dbs.perst;

import java.io.File;
import java.io.FileNotFoundException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Iterator;

import org.garret.perst.Persistent;
import org.garret.perst.Storage;
import org.garret.perst.StorageFactory;

import com.flca.frw.dao.Dao;
import com.flca.frw.dao.PersistenceType;

public class PerstDaoImpl implements Dao {

	private static final long serialVersionUID = 6859145341274345648L;

	// TODO map maken om meerdere perst dbs te ondersteunen
	private static Storage storage;
	private static Persistent root;

	public PerstDaoImpl() {
	}

	/**
	 * This method is called when starting up the application via the
	 * BootStrapper
	 * 
	 * @param aRegDbs
	 */
	public static void initStorage(PerstDatabase aRegDbs) {
		if (storage == null) {
			try {
				storage = StorageFactory.getInstance().createStorage();

				if (storageExist(aRegDbs) || !aRegDbs.databaseMustExists()) {
					openDatabase(aRegDbs);
				} else {
					throw new FileNotFoundException("Perst database " + aRegDbs.getUrl() + " not found");
				}

			} catch (Exception e) {
				logError("error creating perst connection mgr " + e);
				throw new RuntimeException(e);
			}
		}
	}

	private static void openDatabase(PerstDatabase aRegDbs) throws NoSuchMethodException, InstantiationException,
			IllegalAccessException, InvocationTargetException {
		storage.open(aRegDbs.getUrl(), getPoolSize(aRegDbs));

		Class<?> rootClass = aRegDbs.getStorageRoot();
		root = (Persistent) storage.getRoot();
		if (root == null) {
			Constructor<?> ctor = rootClass.getConstructor(new Class[] { Storage.class });
			root = (Persistent) ctor.newInstance(storage);
			storage.setRoot(root); // register root object
		}
	}

	private static boolean storageExist(PerstDatabase dbsProps) {
		File f = new File(dbsProps.getUrl());
		return f.exists();
	}

	public Object getObjectMgr() {
		return new PerstDao(storage, root);
	}

	public boolean isInTransaction() throws Exception {
		return true;
	}

	public void commit() throws Exception {
	}

	public void rollback() throws Exception {
	}

	public void startTransaction() throws Exception {
	}

	public Iterator<Object> openByQuery(Class<?> aClass, String aSql, Object[] aArguments) throws Exception {
		return null;
	}

	public Iterator<Object> openByQuery(String aSql, Object[] aArguments) throws Exception {
		return null;
	}

	public void deleteAll(Class<?> aClass) throws Exception {
		System.out.println("not implemented yet");
	}

	public PersistenceType technology() {
		return PersistenceType.PERST;
	}

	private static void logError(String msg) {
		System.out.println(msg);
	}

	public static Storage getStorage() {
		return storage;
	}

	public static Persistent getRoot() {
		return root;
	}

	public Dao buildDao(PerstDatabase aRegDbs) {
		try {
			if (aRegDbs != null) {
				initStorage(aRegDbs);
				return new PerstDaoImpl();
			} else {
				throw new Exception("input is not an IRegisterPerstDbs implementation");
			}
		} catch (Exception e) {
			logError("error creating perst connection mgr " + e);
			throw new RuntimeException(e);
		}
	}

	static final int DEFAULT_POOL_SIZE = 8 * 1024 * 1024; // default database
															// cache size

	private static long getPoolSize(PerstDatabase aRegDbs) {
		if (aRegDbs.getPerstPoolsize() > 0) {
			return aRegDbs.getPerstPoolsize();
		}
		return DEFAULT_POOL_SIZE;
	}

}
